import java.util.Arrays;
import java.util.Comparator;
import java.util.stream.IntStream;

// 0-1 背包问题
// 可能得不到最优解
public class KnapsackProblem {
    public static void main(String[] args) {
        Item[] items = new Item[]{
                new Item(1, "黄金", 4, 1600),
                new Item(2, "宝石", 8, 2400),
                new Item(3, "白银", 5, 30),
                new Item(4, "钻石", 1, 10_000),
        };
        System.out.println(select2(items, 10));
    }

    static class Item {
        int index;
        int weight;
        int value;
        String name;

        public Item(int index, String name, int weight, int value) {
            this.index = index;
            this.name = name;
            this.weight = weight;
            this.value = value;
        }

        public int unitValue() {
            return value / weight;
        }

        @Override
        public String toString() {
            return "Item(" + index + ")";
        }
    }

    // 贪心算法实现
    public static void select1(Item[] items, int total){
        Arrays.sort(items, Comparator.comparingInt(Item::unitValue));
        int max = 0;
        for (Item item : items){
            if (total >= item.weight){
                total -= item.weight;
                max += item.value;
            }
        }
    }

    // 动态规划方法实现
    // 二维数组实现
    public static int select(Item[] item, int total){
        int[][] dp = new int[item.length][total + 1];
        Item item1 = item[0];
        for (int j = 1; j < total + 1; j++){
            if (j > item1.weight){
                dp[0][j] = item1.value;
            }else {
                dp[0][j] = 0;
            }
        }
        for (int i = 1; i < item.length; i++){
            Item items = item[i];
            for (int j = 1; j < total + 1; j++){
                if (j >= items.weight){
                    dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - items.weight] + items.value);
                }else {
                    dp[i][j] = dp[i - 1][j];
                }
            }
        }
        print(dp);
        return dp[dp.length - 1][total];
    }

    // 一维数组实现
    public static int select2(Item[] item, int total){
        int[] dp = new int[total + 1];
        Item item1 = item[0];
        for (int j = 1; j < total + 1; j++){
            if (j > item1.weight){
                dp[j] = item1.value;
            }else {
                dp[j] = 0;
            }
        }
        for (int i = 1; i < item.length; i++){
            Item item2 = item[i];
            for (int j = total; j > 0; j--){
                if (j >= item2.weight){
                    dp[j] = Math.max(dp[j], item2.value + dp[j - item2.weight]);
                }
            }
        }
        System.out.println(Arrays.toString(dp));
        return dp[total];
    }

    static void print(int[][] dp) {
        System.out.println("   " + "-".repeat(63));
        Object[] array = IntStream.range(0, dp[0].length + 1).boxed().toArray();
        System.out.printf(("%5d ".repeat(dp[0].length)) + "%n", array);
        for (int[] d : dp) {
            array = Arrays.stream(d).boxed().toArray();
            System.out.printf(("%5d ".repeat(d.length)) + "%n", array);
        }
    }
}
